package com.benshou.bcss.common.rest;

import com.alibaba.ttl.TransmittableThreadLocal;
import com.benshou.bcss.common.j17common.annotation.NotNull;
import com.benshou.bcss.common.j17common.annotation.Nullable;
import com.benshou.bcss.common.property.PropertyManager;
import lombok.NonNull;

import java.time.ZoneId;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Request Headers 上下文
 * <p>
 *     注意，仅存储指定的字段值，不会进行全量存储，需要按需管理。
 * </p>
 *
 * @author <a href="mailto:liuzhenbang@benshou.com">Liu Zhenbang</a>
 */
public class RequestHeaderContext {

    /**
     * 线程本地变量：Request Headers
     */
    private static final ThreadLocal<Map<String, List<?>>> HEADERS_TL =
            TransmittableThreadLocal.withInitial(HashMap::new);

    /**
     * 属性操作管理器：租户
     */
    public static final PropertyManagerImpl<Long> TENANT_ID =
            new PropertyManagerImpl<>("tenantId");

    /**
     * 属性操作管理器：用户编号
     */
    public static final PropertyManagerImpl<String> USER_NO =
            new PropertyManagerImpl<>("userNo");

    /**
     * 属性操作管理器：时区
     */
    public static final PropertyManagerImpl<ZoneId> TIMEZONE =
            new PropertyManagerImpl<>("timezone");

    /**
     * 清除所有的 Request Headers 上下文
     */
    public static void clearAll() {
        HEADERS_TL.get().clear();
    }

    /**
     * 获取 Request Headers 中的时区 Id 。如无，返回系统默认时区 Id 。
     *
     * @return 时区 Id
     */
    public static ZoneId getTimezoneDefaultSystem() {
        var timezone = TIMEZONE.getFirst();
        return timezone == null ? ZoneId.systemDefault() : timezone;
    }

    /**
     * 属性管理器
     */
    public static class PropertyManagerImpl<T> implements PropertyManager<List<T>> {

        /**
         * Key
         */
        @NonNull
        private final String key;

        PropertyManagerImpl(@NotNull String key) {
            this.key = key;
        }

        /**
         * 设置单例
         *
         * @param singleton 单例
         */
        public void setSingleton(T singleton) {
            set(singleton == null ? null : List.of(singleton));
        }

        /**
         * @return 首例
         */
        @Nullable
        public T getFirst() {
            var value = get();
            return value == null || value.isEmpty() ? null : value.get(0);
        }

        @Override
        public void set(List<T> property) {
            HEADERS_TL.get().put(this.key, property == null ? null : List.copyOf(property));
        }

        @Override
        public @Nullable List<T> get() {
            @SuppressWarnings("unchecked")
            var result = (List<T>) HEADERS_TL.get().get(this.key);
            return result;
        }

        @Override
        public void remove() {
            HEADERS_TL.get().remove(this.key);
        }
    }
}
